package cn.yao.hotel.service.impl;

import cn.yao.hotel.mapper.HotelMapper;
import cn.yao.hotel.pojo.Hotel;
import cn.yao.hotel.pojo.HotelDoc;
import cn.yao.hotel.pojo.PageResult;
import cn.yao.hotel.pojo.RequestParams;
import cn.yao.hotel.service.IHotelService;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.util.ObjectBuilder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;


@Slf4j
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Override
    public PageResult search(RequestParams params) {
            log.info("params:{}", JSONObject.toJSONString(params));

            try {
                SearchRequest.Builder request=new SearchRequest.Builder().index("hotel");
                // 1.准备Request
                buildBasicQuery(params,request);

                String location = params.getLocation();

                Integer page = params.getPage();
                Integer size = params.getSize();
                request.from((page-1)*size).size(size);

                if(!StringUtils.isEmpty(location)) {
                    request.sort(new Function<SortOptions.Builder, ObjectBuilder<SortOptions>>() {
                        @Override
                        public ObjectBuilder<SortOptions> apply(SortOptions.Builder builder) {
                            builder.geoDistance(new Function<GeoDistanceSort.Builder, ObjectBuilder<GeoDistanceSort>>() {
                                @Override
                                public ObjectBuilder<GeoDistanceSort> apply(GeoDistanceSort.Builder builder) {
                                    builder.field("location").location(Arrays.asList(GeoLocation.of(new Function<GeoLocation.Builder, ObjectBuilder<GeoLocation>>() {
                                        @Override
                                        public ObjectBuilder<GeoLocation> apply(GeoLocation.Builder builder) {

                                            return builder.text(location);
                                        }
                                    }))).order(SortOrder.Asc).unit(DistanceUnit.Kilometers);
                                    return null;
                                }
                            });
                            return null;
                        }
                    });
                }
                SearchRequest build = request.build();
                log.info("{}",build);
                SearchResponse<HotelDoc> search = elasticsearchClient.search(build, HotelDoc.class);
                System.out.println(search);

                return handleResponse(search);
            }catch (Exception e){
                log.error("{}",e);
            }

        return new PageResult();
    }

//    @Autowired
//    private RestHighLevelClient restHighLevelClient;
//
//    @Override
//    public PageResult search(RequestParams params) {
//        log.info("{}");
//        try {
//            // 1.准备Request
//            SearchRequest request = new SearchRequest("hotel");

//            // 2.准备请求参数
//            // 2.1.query
//            buildBasicQuery(params, request);
//            // 2.2.分页
//            int page = params.getPage();
//            int size = params.getSize();
//            request.source().from((page - 1) * size).size(size);
//            // 2.3.距离排序
//            String location = params.getLocation();
//            if (StringUtils.isNotBlank(location)) {
//                request.source().sort(SortBuilders
//                        .geoDistanceSort("location", new GeoPoint(location))
//                        .order(SortOrder.ASC)
//                        .unit(DistanceUnit.KILOMETERS)
//                );
//            }
//            // 3.发送请求
////            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//            SearchResponse response = null;
//            // 4.解析响应
//            return handleResponse(response);
//        } catch (Exception e) {
//            throw new RuntimeException("搜索数据失败", e);
//        }
//    }
//
    private void buildBasicQuery(RequestParams params,SearchRequest.Builder request){
        BoolQuery.Builder boolQuerybuilder = new BoolQuery.Builder();
        String key = params.getKey();
        if(!StringUtils.isEmpty(key)){
            boolQuerybuilder.must( Query.of(builder -> builder.match(MatchQuery.of(builder1 -> builder1.field("key").query(key)))));
        }else{
            boolQuerybuilder.must( Query.of(builder -> builder.matchAll(MatchAllQuery.of(builder1 -> builder1))));
        }
        String brand = params.getBrand();

        if(!StringUtils.isEmpty(brand)){
            boolQuerybuilder.filter(Query.of(builder -> builder.term(TermQuery.of(builder1 -> builder1.field("brand").value(brand)))));

        }


        String city = params.getCity();
        if(!StringUtils.isEmpty(city)){

            boolQuerybuilder.filter(Query.of(builder -> builder.term(TermQuery.of(builder1 -> builder1.field("city").value(city)))));
        }

        String starName = params.getStarName();
        if(!StringUtils.isEmpty(starName)){
            boolQuerybuilder.filter(Query.of(builder -> builder.term(TermQuery.of(builder1 -> builder1.field("starName").value(starName)))));
        }


        Integer minPrice = params.getMinPrice();
        Integer maxPrice = params.getMaxPrice();

        ObjectBuilder<Query> bool = new Query.Builder().bool(boolQuerybuilder.build());

        FunctionScore.Builder functionScoreBuilder = new FunctionScore.Builder();

        functionScoreBuilder.filter(Query.of(builder -> builder.term(TermQuery.of(builder1 -> builder1.field("isAD").value(true)))))
                .weight(10d);

        FunctionScoreQuery functionScoreQuery = new FunctionScoreQuery.Builder()
                .query(bool.build())
                .functions(Arrays
                .asList(functionScoreBuilder.build()))
                .build();

        request.query(new Query.Builder().functionScore(functionScoreQuery).build());

        log.info("{}",functionScoreQuery);


    }
//    private void buildBasicQuery(RequestParams params, SearchRequest request) {
//        // 1.准备Boolean查询
//        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//
//        // 1.1.关键字搜索，match查询，放到must中
//        String key = params.getKey();
//        if (StringUtils.isNotBlank(key)) {
//            // 不为空，根据关键字查询
//            boolQuery.must(QueryBuilders.matchQuery("all", key));
//        } else {
//            // 为空，查询所有
//            boolQuery.must(QueryBuilders.matchAllQuery());
//        }
//
//        // 1.2.品牌
//        String brand = params.getBrand();
//        if (StringUtils.isNotBlank(brand)) {
//            boolQuery.filter(QueryBuilders.termQuery("brand", brand));
//        }
//        // 1.3.城市
//        String city = params.getCity();
//        if (StringUtils.isNotBlank(city)) {
//            boolQuery.filter(QueryBuilders.termQuery("city", city));
//        }
//        // 1.4.星级
//        String starName = params.getStarName();
//        if (StringUtils.isNotBlank(starName)) {
//            boolQuery.filter(QueryBuilders.termQuery("starName", starName));
//        }
//        // 1.5.价格范围
//        Integer minPrice = params.getMinPrice();
//        Integer maxPrice = params.getMaxPrice();
//        if (minPrice != null && maxPrice != null) {
//            maxPrice = maxPrice == 0 ? Integer.MAX_VALUE : maxPrice;
//            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
//        }
//
//        // 2.算分函数查询
//        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
//                boolQuery, // 原始查询，boolQuery
//                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
//                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//                                QueryBuilders.termQuery("isAD", true), // 过滤条件
//                                ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
//                        )
//                }
//        );
//
//        // 3.设置查询条件
//        request.source().query(functionScoreQuery);
//    }
//
    private PageResult handleResponse(SearchResponse<HotelDoc> response) {
        HitsMetadata<HotelDoc> hitsMetadata = response.hits();

        // 4.1.总条数
        long total = hitsMetadata.total().value();
        // 4.2.获取文档数组
        List<Hit<HotelDoc>>  hits =  hitsMetadata.hits();


        // 4.3.遍历
        List<HotelDoc> hotels = new ArrayList<>(hits.size());
        for (Hit<HotelDoc> hit : hits) {
            // 4.4.获取source
            HotelDoc hotelDoc = hit.source();
            // 4.5.反序列化，非高亮的
            // 4.6.处理高亮结果
            // 1)获取高亮map

            Map<String, List<String>> highlight = hit.highlight();

//            Map<String, HighlightField> map = hit.getHighlightFields();
            if (highlight != null && !highlight.isEmpty()) {
                // 2）根据字段名，获取高亮结果
                List<String> highlightField = highlight.get("name");
                if (highlightField != null) {
                    // 3）获取高亮结果字符串数组中的第1个元素
                    String hName = highlightField.get(0).toString();
                    // 4）把高亮结果放到HotelDoc中
                    hotelDoc.setName(hName);
                }
            }

            // 4.8.排序信息
            List<String> sort = hit.sort();
            if(sort.size()>0){
                hotelDoc.setDistance(sort.get(0));
            }

            // 4.9.放入集合
            hotels.add(hotelDoc);
        }
        return new PageResult(total, hotels);
    }
}
